package com.game.doudizhu.model;

import com.game.doudizhu.common.constants.GameConstants;
import lombok.Data;

import java.util.*;

@Data
public class GameState {
    private GameRoom room;
    private Long roomId;
    private String status;
    private Long currentPlayerId;
    private Long landlordId;
    private Map<Long, List<Card>> playerCards = new HashMap<>();
    private List<Card> lastPlayedCards;
    private Long lastPlayerId;
    private boolean canPass;
    private int currentBid;
    private int baseScore;
    private int multiple;
    private int bombCount;
    private boolean biddingPhase;
    private Map<Long, Integer> playerScores;
    private Map<Long, Integer> bidScores;
    private boolean bidding;
    private Long currentBidder;
    private List<Card> landlordCards;
    private boolean playing = false;
    private boolean gameOver = false;
    private Map<Long, Integer> playerBids = new HashMap<>();
    private int maxBid = 0;
    private boolean landlordRevealed = false;
    private List<GameAction> gameActions = new ArrayList<>();
    private Long winner;
    private boolean needRestart;

    public GameState() {
        this.playerCards = new HashMap<>();
        this.lastPlayedCards = new ArrayList<>();
        this.playerScores = new HashMap<>();
        this.bidScores = new HashMap<>();
        this.playerBids = new HashMap<>();
        this.landlordCards = new ArrayList<>();
        this.gameActions = new ArrayList<>();
        this.canPass = false;
        this.currentBid = 0;
        this.baseScore = 1;
        this.multiple = 1;
        this.bombCount = 0;
        this.biddingPhase = true;
        this.bidding = true;
        this.playing = false;
        this.gameOver = false;
        this.maxBid = 0;
        this.landlordRevealed = false;
    }

    public GameState(GameRoom room) {
        this();
        this.room = room;
        this.roomId = room.getId();
        this.status = room.getStatus();

        for (Player player : room.getPlayers()) {
            playerCards.put(player.getId(), new ArrayList<>());
            playerScores.put(player.getId(), 0);
            bidScores.put(player.getId(), 0);
            playerBids.put(player.getId(), -1);
        }

        List<Player> players = new ArrayList<>(room.getPlayers());
        Collections.shuffle(players);
        this.currentBidder = players.get(0).getId();

        GameAction startAction = GameAction.createGameStartAction();
        this.addGameAction(startAction);
    }

    public GameState(GameState other) {
        this.room = other.room;
        this.roomId = other.roomId;
        this.status = other.status;
        this.currentPlayerId = other.currentPlayerId;
        this.landlordId = other.landlordId;
        this.playerCards = new HashMap<>(other.playerCards);
        this.lastPlayedCards = new ArrayList<>(other.lastPlayedCards);
        this.lastPlayerId = other.lastPlayerId;
        this.canPass = other.canPass;
        this.currentBid = other.currentBid;
        this.baseScore = other.baseScore;
        this.multiple = other.multiple;
        this.bombCount = other.bombCount;
        this.biddingPhase = other.biddingPhase;
        this.playerScores = new HashMap<>(other.playerScores);
        this.bidScores = new HashMap<>(other.bidScores);
        this.bidding = other.bidding;
        this.currentBidder = other.currentBidder;
        this.landlordCards = new ArrayList<>(other.landlordCards);
        this.playing = other.playing;
        this.gameOver = other.gameOver;
        this.playerBids = new HashMap<>(other.playerBids);
        this.maxBid = other.maxBid;
        this.landlordRevealed = other.landlordRevealed;
        this.gameActions = new ArrayList<>(other.gameActions);
        this.winner = other.winner;
    }

    public static GameState createNewRound(GameRoom room) {
        GameState gameState = new GameState(room);
        gameState.setBidding(true);
        gameState.setPlaying(false);
        gameState.setGameOver(false);
        gameState.setMaxBid(0);
        gameState.setLandlordRevealed(false);
        gameState.setBombCount(0);
        gameState.setMultiple(1);
        gameState.setBaseScore(1);
        return gameState;
    }

    public void reset() {
        this.playerCards.clear();
        this.lastPlayedCards.clear();
        this.playerScores.clear();
        this.bidScores.clear();
        this.playerBids.clear();
        this.landlordCards.clear();
        this.gameActions.clear();

        this.canPass = false;
        this.currentBid = 0;
        this.baseScore = 1;
        this.multiple = 1;
        this.bombCount = 0;
        this.biddingPhase = true;
        this.bidding = true;
        this.playing = false;
        this.gameOver = false;
        this.maxBid = 0;
        this.landlordRevealed = false;

        if (room != null) {
            for (Player player : room.getPlayers()) {
                playerCards.put(player.getId(), new ArrayList<>());
                playerScores.put(player.getId(), 0);
                bidScores.put(player.getId(), 0);
                playerBids.put(player.getId(), -1);
            }
        }
    }

    public boolean canStartGame() {
        return room != null &&
                room.getPlayers().size() == GameConstants.Room.MAX_PLAYERS &&
                room.getPlayers().stream().allMatch(Player::isReady);
    }

    public boolean isAllPlayersBid() {
        return playerBids.values().stream().noneMatch(bid -> bid == -1);
    }

    public boolean isGameOver() {
        return playerCards.values().stream().anyMatch(List::isEmpty);
    }

    public Long getWinner() {
        return winner;
    }

    public void setWinner(Long winner) {
        this.winner = winner;
    }

    public int getBaseScore() {
        return baseScore;
    }

    public void setBaseScore(int baseScore) {
        this.baseScore = baseScore;
    }

    public int getTotalScore() {
        return baseScore * multiple;
    }

    public int getBombCount() {
        return bombCount;
    }

    public void incrementBombCount() {
        this.bombCount++;
        this.multiple *= 2;
    }

    public Map<Long, Integer> getPlayerScores() {
        return playerScores;
    }

    public void updatePlayerScore(Long playerId, int score) {
        playerScores.put(playerId, playerScores.getOrDefault(playerId, 0) + score);
    }

    public String getPlayerRole(Long playerId) {
        if (playerId.equals(landlordId)) {
            return GameConstants.Role.LANDLORD;
        }
        return GameConstants.Role.FARMER;
    }

    public Map<Long, Integer> getBidScores() {
        return bidScores;
    }

    public List<Card> getLandlordCards() {
        return landlordCards;
    }

    public boolean isLandlordCardsDealt() {
        return landlordId != null && playerCards.get(landlordId).containsAll(landlordCards);
    }

    public int getPlayerCardCount(Long playerId) {
        List<Card> cards = playerCards.get(playerId);
        return cards != null ? cards.size() : 0;
    }

    public void addGameAction(GameAction action) {
        if (gameActions == null) {
            gameActions = new ArrayList<>();
        }
        gameActions.add(action);

        if (gameActions.size() > 100) {
            gameActions.remove(0);
        }
    }

    public GameAction getLastAction() {
        if (gameActions == null || gameActions.isEmpty()) {
            return null;
        }
        return gameActions.get(gameActions.size() - 1);
    }

    public void clearGameActions() {
        if (gameActions != null) {
            gameActions.clear();
        }
    }

    public GameRoom getRoom() {
        return room;
    }

    public boolean isNeedRestart() {
        return needRestart;
    }

    public void setNeedRestart(boolean needRestart) {
        this.needRestart = needRestart;
    }

    public List<Card> getPlayerCardsById(Long playerId) {
        return playerCards.computeIfAbsent(playerId, k -> new ArrayList<>());
    }

    public void setPlayerCards(Long playerId, List<Card> cards) {
        playerCards.put(playerId, new ArrayList<>(cards));
    }
} 